﻿namespace Centido.Core.Test
{
    using System;
    using System.Data.Common;
    using System.Linq;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Testing basic column functionality
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [TestClass]
    public abstract class ColumnBaseTest<T>
    {
        /// <summary>
        /// Domain name
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        /// Column
        /// </summary>
        protected Column GenericColumn { get; set; }

        /// <summary>
        /// Another column of the same type
        /// </summary>
        protected Column AnotherGenericColumn { get; set; }

        /// <summary>
        /// Column name
        /// </summary>
        protected string ColumnName { get; set; }

        /// <summary>
        /// Domain
        /// </summary>
        protected Domain Domain { get; set; }

        /// <summary>
        /// Table
        /// </summary>
        protected Table Table { get; set; }

        /// <summary>
        /// Table name
        /// </summary>
        protected string TableName { get; set; }


        /// <summary>
        /// Column with default value
        /// </summary>
        private IDefaultable<T> ColumnWithDefaultValue
        {
            get { return GenericColumn as IDefaultable<T>; }
        }

        /// <summary>
        /// Another column with default value
        /// </summary>
        private IDefaultable<T> AnotherColumnWithDefaultValue
        {
            get { return AnotherGenericColumn as IDefaultable<T>; }
        }

        /// <summary>
        /// Column with bounds
        /// </summary>
        private IBoundable<T> ColumnWithBounds
        {
            get { return GenericColumn as IBoundable<T>; }
        }

        /// <summary>
        /// Get column type
        /// </summary>
        /// <returns></returns>
        protected abstract ColumnType GetColumnType();

        /// <summary>
        /// Get large value
        /// </summary>
        /// <returns></returns>
        protected abstract T GetLargeValue();

        /// <summary>
        /// Get small value
        /// </summary>
        /// <returns></returns>
        protected abstract T GetSmallValue();

        /// <summary>
        /// Convert db value
        /// </summary>
        /// <param name="dbValue">Db value</param>
        /// <returns></returns>
        protected abstract T ConvertDbValue(object dbValue);

        /// <summary>
        /// Checking that two values are equal
        /// </summary>
        /// <param name="firstValue">First value</param>
        /// <param name="secondValue">Second value</param>
        protected virtual bool ValuesAreEqual(object firstValue, object secondValue)
        {
            if (firstValue == null && secondValue == null)
                return true;
            
            return firstValue != null && firstValue.Equals(secondValue);
        }

        /// <summary>
        /// Test initialize
        /// </summary>
        [TestInitialize]
        public void TestInitialize()
        {
            Domain = Domain.Build(DomainConfiguration.Load(DomainName));

            Domain.DeleteAllTables();

            Table = Domain.AddTable(StringProperty.RandomIdentifier());

            //Immedialtelly renaming table (for complexity)
            Table.Name = (TableName = StringProperty.RandomIdentifier());

            Table.GrantAll();

            GenericColumn = Table.AddColumn(StringProperty.RandomIdentifier(), GetColumnType());
            AnotherGenericColumn = Table.AddColumn(StringProperty.RandomIdentifier(), GetColumnType());

            //Immediatelly renaming column (for complexity)
            GenericColumn.Name = (ColumnName = StringProperty.RandomIdentifier());

            //Changing IsNotNull (this is usefull - because in some rdbms this operation pulls out the column type)
            //E.g. alter table t alter column c COLUMN_TYPE!!! not null
            GenericColumn.IsNotNull = true;
            GenericColumn.IsNotNull = false;
        }

        /// <summary>
        /// Test clean up
        /// </summary>
        [TestCleanup]
        public void TestCleanUp()
        {
            Domain.DeleteAllTables();
        }

        /// <summary>
        /// Creating a column
        /// </summary>
        [TestMethod]
        public void CreatingColumn()
        {
            //Column name
            var columnName = StringProperty.Identifier('a');

            var initialColumnCount = Table.Columns.Count();

            //Creating string column
            var column = Table.AddColumn(columnName, GetColumnType());

            Assert.IsNotNull(column);
            Assert.IsNotNull(column.Name);
            Assert.AreEqual(column.Name, columnName);
            Assert.AreEqual(column.ColumnType, GetColumnType());
            Assert.IsFalse(column.IsSystem);

            //Searching for the column
            Assert.IsNotNull(Table.GetColumn(columnName));
            Assert.IsTrue(Table.Columns.Any(c => c.Name == columnName));
            Assert.IsTrue(GenericColumn.Exists());

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount + 1);
        }

        /// <summary>
        /// Creating large number of columns
        /// </summary>
        [TestMethod]
        public void CreatingLargeNumberOfColumns()
        {
            var initialColumnCount = Table.Columns.Count();

            for (var i = 0; i < 50; i++)
            {
                Table.AddColumn("c1234567891234567890123" + i, GetColumnType());
            }

            Assert.AreEqual(initialColumnCount + 50, Table.Columns.Count());
        }

        /// <summary>
        /// Passing null column name when creating a column
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void PassingNullColumnName()
        {
            Table.AddColumn(null, GetColumnType());
        }

        /// <summary>
        /// Passing empty column name
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void PassingEmptyColumnName()
        {
            Table.AddColumn(" \t", GetColumnType());
        }

        /// <summary>
        /// Passing forbidden symbol
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void PassingForbiddenSymbol()
        {
            Table.AddColumn("a a", GetColumnType());
        }

        /// <summary>
        /// Passing wrong start symbol
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void PassingWrongStartSymbol()
        {
            Table.AddColumn("_a", GetColumnType());
        }

        /// <summary>
        /// Checking that column
        /// </summary>
        [TestMethod]
        public void CheckingColumnNameTrimmingAndLowering()
        {
            //Not trimmed upper case name
            var column = Table.AddColumn("\t A\t ", GetColumnType());

            //Checking that column was trimmed and lowered
            Assert.AreEqual(column.Name, "a");
        }

        /// <summary>
        /// Passing too long column name when creating new column
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException), AllowDerivedTypes = true)]
        public void PassingTooLongColumnName()
        {
            //Too long column name
            Table.AddColumn(StringProperty.TooLongStandardValue('a'), GetColumnType());
        }

        /// <summary>
        /// Passing a keyword when creating a column
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void PassingKeywordColumnName()
        {
            Table.AddColumn("SeLeCT", GetColumnType());
        }


        /// <summary>
        ///   Passing non-english letters when creating a column.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void PassingNonEnglishColumnName()
        {
            Table.AddColumn("яяя", GetColumnType());
        }


        /// <summary>
        /// Adding column with duplicate id
        /// </summary>
        [TestMethod]
        public void AddingColumnWithDuplicateId()
        {
            try
            {
                Table.AddColumn(GenericColumn.Id, StringProperty.RandomIdentifier(), ColumnType.Boolean);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.AddingColumnWithDuplicateId);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Adding column with duplicate name in one table
        /// </summary>
        [TestMethod]
        public void AddingColumnWithDuplicateNameInOneTable()
        {
            //Column name
            const string columnName = "column_name";

            //Adding the first column
            Table.AddColumn(columnName, ColumnType.Boolean);

            try
            {
                //Adding the second column
                Table.AddColumn(columnName, GetColumnType());
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.AddingColumnWithDuplicateName);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Adding column with duplicate names into two different tables
        /// </summary>
        [TestMethod]
        public void AddingColumnWithDuplicateNameInTwoTables()
        {
            //Creating new tables
            var firstTable = Domain.AddTable("first_table");
            var secondTable = Domain.AddTable("second_table");

            //Column name
            const string columnName = "column_name";

            var firstColumn = firstTable.AddColumn(columnName, GetColumnType());
            var secondColumn = secondTable.AddColumn(columnName, GetColumnType());

            Assert.IsNotNull(firstColumn);
            Assert.IsNotNull(secondColumn);
            Assert.IsTrue(firstColumn.Exists());
            Assert.IsTrue(secondColumn.Exists());

            //Removing second table and column
            secondTable.Delete();

            //Writing to some property in first column
            firstColumn.DisplayName = "display name";

            Assert.AreEqual(firstColumn.DisplayName, "display name");
        }

        /// <summary>
        /// Adding two columns
        /// </summary>
        [TestMethod]
        public void AddingTwoColumnsInOneTable()
        {
            var initialColumnCount = Table.Columns.Count();

            //Creating columns
            var firstColumn = Table.AddColumn("first_column", GetColumnType());
            var secondColumn = Table.AddColumn("second_column", GetColumnType());

            Assert.IsNotNull(firstColumn);
            Assert.IsNotNull(secondColumn);
            Assert.AreEqual(firstColumn.Name, "first_column");
            Assert.AreEqual(secondColumn.Name, "second_column");

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount + 2);

            //Removing the second column
            secondColumn.Delete();

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount + 1);
        }

        /// <summary>
        /// Searching column by id
        /// </summary>
        [TestMethod]
        public void SearchingColumnById()
        {
            //Adding columns
            var firstColumn = Table.AddColumn("first_column", GetColumnType());

            var secondColumnId = Guid.NewGuid();
            Table.AddColumn(secondColumnId, "second_column", GetColumnType());

            //Searching columns by id
            Assert.IsNotNull(Table.GetColumn(firstColumn.Id));
            Assert.AreEqual(Table.GetColumn(firstColumn.Id).Id, firstColumn.Id);
            Assert.IsNotNull(Table.GetColumn(secondColumnId));
            Assert.AreEqual(Table.GetColumn(secondColumnId).Id, secondColumnId);

            //Searching non-existent column id
            Assert.IsNull(Table.GetColumn(Guid.NewGuid()));
        }

        /// <summary>
        /// Searching column by name
        /// </summary>
        [TestMethod]
        public void SearchingColumnByName()
        {
            //Searching columns by name
            Assert.IsNotNull(Table.GetColumn(GenericColumn.Name));
            Assert.AreEqual(Table.GetColumn(GenericColumn.Name).Name, GenericColumn.Name);

            //Searching non-existent column id
            Assert.IsNull(Table.GetColumn("aaaa"));
        }

        /// <summary>
        ///   Ensuring column by name
        /// </summary>
        [TestMethod]
        public void EnsuringColumnByName()
        {
            var initialColumnCount = Table.Columns.Count();

            //Creating a new column
            var newColumnName = StringProperty.RandomIdentifier();
            var newColumn = Table.EnsureColumn(newColumnName, GetColumnType());

            Assert.IsNotNull(newColumn);
            Assert.AreEqual(newColumn.Name, newColumnName.ToLower());
            Assert.AreEqual(newColumn.ColumnType, GetColumnType());
            Assert.AreEqual(initialColumnCount + 1, Table.Columns.Count());

            //Trying to create existing column
            var column = Table.EnsureColumn(ColumnName, GetColumnType());

            Assert.IsNotNull(column);
            Assert.AreEqual(column.Name, ColumnName.ToLower());
            Assert.AreEqual(column.ColumnType, GetColumnType());
            Assert.AreEqual(initialColumnCount + 1, Table.Columns.Count());
        }


        /// <summary>
        ///  Ensuring column by name with wrong column type
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DbOperationException))]
        public void EnsuringColumnByNameWithWrongColumnType()
        {
            var columnType = ColumnType.Integer;

            if (columnType == GetColumnType())
                columnType = ColumnType.String;

            Table.EnsureColumn(ColumnName, columnType);
        }


        /// <summary>
        ///   Ensuring column by id and name
        /// </summary>
        [TestMethod]
        public void EnsuringColumnByIdAndName()
        {
            var initialColumnCount = Table.Columns.Count();

            //Creating a new column
            var newColumnId = Guid.NewGuid();
            var newColumnName = StringProperty.RandomIdentifier();
            var newColumn = Table.EnsureColumn(newColumnId, newColumnName, GetColumnType());

            Assert.IsNotNull(newColumn);
            Assert.AreEqual(newColumn.Id, newColumnId);
            Assert.AreEqual(newColumn.Name, newColumnName.ToLower());
            Assert.AreEqual(newColumn.ColumnType, GetColumnType());
            Assert.AreEqual(initialColumnCount + 1, Table.Columns.Count());

            //Trying to create existing column
            var column = Table.EnsureColumn(GenericColumn.Id, ColumnName, GetColumnType());

            Assert.IsNotNull(column);
            Assert.AreEqual(column.Id, GenericColumn.Id);
            Assert.AreEqual(column.Name, ColumnName.ToLower());
            Assert.AreEqual(column.ColumnType, GetColumnType());
            Assert.AreEqual(initialColumnCount + 1, Table.Columns.Count());
        }


        /// <summary>
        ///   Ensuring a column by id and name with a wrong id
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DbOperationException))]
        public void EnsuringColumnByIdAndNameWithWrongId()
        {
            Table.EnsureColumn(Guid.NewGuid(), ColumnName, GetColumnType());
        }


        /// <summary>
        ///   Ensuring a column by id and name with a wrong name
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DbOperationException))]
        public void EnsuringColumnByIdAndNameWithWrongName()
        {
            Table.EnsureColumn(GenericColumn.Id, StringProperty.RandomIdentifier(), GetColumnType());
        }


        /// <summary>
        ///   Ensuring a column by id and name with a wrong type
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DbOperationException))]
        public void EnsuringColumnByIdAndNameWithWrongType()
        {
            var columnType = ColumnType.Integer;

            if (columnType == GetColumnType())
                columnType = ColumnType.String;

            Table.EnsureColumn(GenericColumn.Id, ColumnName, columnType);
        }

        /// <summary>
        /// Removing a column
        /// </summary>
        [TestMethod]
        public void RemovingColumnByName()
        {
            var initialColumnCount = Table.Columns.Count();

            //Column name
            var columnName = StringProperty.RandomIdentifier();

            var column = Table.AddColumn(columnName, GetColumnType());

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount + 1);

            //Removing the column
            Table.DeleteColumn(columnName);

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount);

            //Ensuring that there is no such a column
            Assert.IsNull(Table.GetColumn(columnName));
            Assert.IsFalse(Table.Columns.Any(c => c.Name == columnName));
            Assert.IsFalse(column.Exists());
        }

        /// <summary>
        /// Removing column by id
        /// </summary>
        [TestMethod]
        public void RemovingColumnById()
        {
            var initialColumnCount = Table.Columns.Count();

            //Column name
            var columnName = StringProperty.RandomIdentifier();

            var column = Table.AddColumn(columnName, GetColumnType());

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount + 1);

            //Removing the column
            Table.DeleteColumn(column.Id);

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount);

            //Ensuring that there is no such a column
            Assert.IsNull(Table.GetColumn(columnName));
            Assert.IsFalse(Table.Columns.Any(c => c.Name == columnName));
            Assert.IsFalse(column.Exists());
        }

        /// <summary>
        /// Removing column by reference
        /// </summary>
        [TestMethod]
        public void RemovingColumnByReference()
        {
            var initialColumnCount = Table.Columns.Count();

            //Column name
            var columnName = StringProperty.RandomIdentifier();

            var column = Table.AddColumn(columnName, GetColumnType());

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount + 1);

            //Removing the column
            Table.DeleteColumn(column);

            //Checking column count
            Assert.AreEqual(Table.Columns.Count(), initialColumnCount);

            //Ensuring that there is no such a column
            Assert.IsNull(Table.GetColumn(columnName));
            Assert.IsFalse(Table.Columns.Any(c => c.Name == columnName));
            Assert.IsFalse(column.Exists());
        }

        /// <summary>
        /// Removing column that doesn't exist
        /// </summary>
        [TestMethod]
        public void RemovingColumnThatDoesntExist()
        {
            //Removing the column
            GenericColumn.Delete();

            Assert.IsFalse(GenericColumn.Exists());

            try
            {
                //Removing that column once again
                GenericColumn.Delete();
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Renaming a column
        /// </summary>
        [TestMethod]
        public void RenamingColumn()
        {
            //Creating new column
            const string initialColumnName = "column_name";

            var column = Table.AddColumn(initialColumnName, GetColumnType());

            Assert.AreEqual(column.Name, initialColumnName);

            //Renaming the column
            var newColumnName = StringProperty.Identifier('a');

            column.Name = newColumnName;

            Assert.AreEqual(column.Name, newColumnName);

            Assert.IsNull(Table.GetColumn(initialColumnName)); // bug fix - checking that the table cache was updated
            Assert.IsNotNull(Table.GetColumn(newColumnName));
        }

        /// <summary>
        /// Renaming column passing null value
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void RenamingColumnPassingNullValue()
        {
            GenericColumn.Name = null;
        }

        /// <summary>
        /// Renaming column passing empty value
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void RenamingColumnPassingEmptyValue()
        {
            GenericColumn.Name = "\t ";
        }

        /// <summary>
        /// Passing a keyword when renaming a column
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void RenamingColumnPassingKeyword()
        {
            GenericColumn.Name = "SELECT";
        }

        /// <summary>
        /// Renaming column with too long name
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException), AllowDerivedTypes = true)]
        public void RenamingColumnWithTooLongName()
        {
            GenericColumn.Name = StringProperty.TooLongIdentifier('a');
        }


        /// <summary>
        ///   Renaming a column using non-english letters.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException), AllowDerivedTypes = true)]
        public void RenamingColumnWithNonEnglishLetters()
        {
            GenericColumn.Name = "я";
        }

        /// <summary>
        /// Renaming column with duplicate name
        /// </summary>
        [TestMethod]
        public void RenamingColumnWithDuplicateName()
        {
            //Creating another column
            var column = Table.AddColumn("another_column", ColumnType.Boolean);

            try
            {
                column.Name = ColumnName;
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.AddingColumnWithDuplicateName);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Renaming a column that doesn't exist
        /// </summary>
        [TestMethod]
        public void RenamingColumnThatDoesntExist()
        {
            //Removing the column
            GenericColumn.Delete();

            try
            {
                //Renaming that column
                GenericColumn.Name = StringProperty.RandomIdentifier();
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Reading and writing column description
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingColumnDescription()
        {
            //Initially description must be null
            Assert.IsNull(GenericColumn.Description);

            //Writing to description
            var description = StringProperty.ExtendedValue('A');

            GenericColumn.Description = description;

            Assert.AreEqual(GenericColumn.Description, description);

            GenericColumn.Refresh();

            Assert.AreEqual(GenericColumn.Description, description);

            //Writing null
            GenericColumn.Description = null;

            Assert.IsNull(GenericColumn.Description);
        }

        /// <summary>
        /// Checking column description trimming
        /// </summary>
        [TestMethod]
        public void CheckingColumnDescriptionTrimming()
        {
            GenericColumn.Description = "\t A\t ";

            Assert.AreEqual(GenericColumn.Description, "A");
        }

        /// <summary>
        /// Passing too long value to column description
        /// </summary>
        [TestMethod]
        public void PassingTooLongValueToColumnDescription()
        {
            try
            {
                GenericColumn.Description = StringProperty.TooLongExtendedValue('A');
            }
            catch (DbException)
            {
                Assert.IsNull(GenericColumn.Description);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Reading and writing display name
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingDisplayName()
        {
            //Initially display name must be null
            Assert.IsNull(GenericColumn.DisplayName);

            //Display name value
            var displayName = StringProperty.StandardValue('A');

            GenericColumn.DisplayName = displayName;

            Assert.AreEqual(GenericColumn.DisplayName, displayName);

            GenericColumn.Refresh();

            Assert.AreEqual(GenericColumn.DisplayName, displayName);

            //Writing null to display name
            GenericColumn.DisplayName = null;

            Assert.IsNull(GenericColumn.DisplayName);
        }

        /// <summary>
        /// Checking display name trimming
        /// </summary>
        [TestMethod]
        public void CheckingDisplayNameTrimming()
        {
            GenericColumn.DisplayName = "\t A\t ";

            Assert.AreEqual(GenericColumn.DisplayName, "A");
        }

        /// <summary>
        /// Passing too long value to column name display name
        /// </summary>
        [TestMethod]
        public void PassingTooLongValueToColumnDisplayName()
        {
            try
            {
                GenericColumn.DisplayName = StringProperty.TooLongStandardValue('A');
            }
            catch (DbException)
            {
                Assert.IsNull(GenericColumn.DisplayName);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Reading customization level
        /// </summary>
        [TestMethod]
        public void ReadingCustomizationLevel()
        {
            Assert.IsTrue(GenericColumn.CustomizationLevel >= 0);
        }

        /// <summary>
        /// Reading and writing order index
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingOrderIndex()
        {
            //Creating another column
            var secondColumn = Table.AddColumn("another_column", GetColumnType());

            //Order indexes of the two columns are not be the same
            Assert.AreNotEqual(GenericColumn.OrderIndex, secondColumn.OrderIndex);

            //But they could be
            secondColumn.OrderIndex = GenericColumn.OrderIndex;

            Assert.AreEqual(GenericColumn.OrderIndex, secondColumn.OrderIndex);
        }

        /// <summary>
        /// Reading and writing IsNotNull (without data)
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingIsNotNullWithoutData()
        {
            //Initially column must be nullable
            Assert.IsFalse(GenericColumn.IsNotNull);

            //Settting that column is not null
            GenericColumn.IsNotNull = true;
            GenericColumn.IsNotNull = true;

            Assert.IsTrue(GenericColumn.IsNotNull);

            //Setting that column nullable
            GenericColumn.IsNotNull = false;
            GenericColumn.IsNotNull = false;

            Assert.IsFalse(GenericColumn.IsNotNull);
        }

        /// <summary>
        /// Setting that column is not null with data inside the column
        /// </summary>
        [TestMethod]
        public void SettingColumnIsNotNullWithDataInsideColumn()
        {
            //Inserting a not null value into the column
            Table.InsertRow(GenericColumn.Value(GetLargeValue()));

            //Setting that column is not null
            GenericColumn.IsNotNull = true;

            Assert.IsTrue(GenericColumn.IsNotNull);
        }

        /// <summary>
        /// Setting column IsNotNull for deleted column
        /// </summary>
        [TestMethod]
        public void SettingColumnIsNotNullForDeletedColumn()
        {
            GenericColumn.Delete();

            try
            {
                GenericColumn.IsNotNull = true;
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Setting that column is not null with null inside the column
        /// </summary>
        [TestMethod]
        public void SettingColumnNotNullWithNullInsideColumn()
        {
            //Inserting a null value into the column
            Table.InsertRow(GenericColumn.Value(null));

            try
            {
                //Trying to set that column is not null
                GenericColumn.IsNotNull = true;
            }
            catch (DbException)
            {
                Assert.IsFalse(GenericColumn.IsNotNull);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Inserting null into a not null column
        /// </summary>
        [TestMethod]
        public void InsertingNullIntoNotNullColumn()
        {
            //Setting the column to be not null
            GenericColumn.IsNotNull = true;

            Assert.IsTrue(GenericColumn.IsNotNull);

            try
            {
                //Trying to insert a null value into the column
                Table.InsertRow(GenericColumn.Value(null));
            }
            catch (DbException)
            {
                Assert.AreEqual(Table.RowCount, 0);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Bug fix: changing the nullability of an indexed column. 
        /// </summary>
        [TestMethod]
        public void SettingColumnNotNullWhenItIsIndexed()
        {
            if (GenericColumn.ColumnType == ColumnType.Binary
                || GenericColumn.ColumnType == ColumnType.Text)
                return;

            // Adding an index
            var index = Table.AddIndex(GenericColumn);

            // Adding another index
            var anotherIndex = Table.AddIndex(Table.SystemColumns.Id, GenericColumn);

            // Inserting a value
            Table.InsertRow(GenericColumn.Value(GetSmallValue()));

            // Setting column to be not null
            GenericColumn.IsNotNull = true;

            Assert.IsTrue(GenericColumn.IsNotNull);
            Assert.IsTrue(index.Exists());
            Assert.IsTrue(anotherIndex.Exists());
        }


        ///////////////////////////////////////////////////////////////////
        // Default values

        /// <summary>
        /// Reading and writing default value
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingDefaultValue()
        {
            if (ColumnWithDefaultValue == null)
            {
                return;
            }

            //Initially default value must be null
            Assert.IsNull(ColumnWithDefaultValue.DefaultValue);

            //Writing default value
            var defaultValue = GetLargeValue();

            ColumnWithDefaultValue.DefaultValue = defaultValue;

            GenericColumn.Refresh();

            Assert.IsTrue(ValuesAreEqual(ColumnWithDefaultValue.DefaultValue, defaultValue));

            // Writing another default value (bug fix)
            ColumnWithDefaultValue.DefaultValue = (defaultValue = GetSmallValue());

            Assert.IsTrue(ValuesAreEqual(ColumnWithDefaultValue.DefaultValue, defaultValue));

            //Writing null to default value
            ColumnWithDefaultValue.DefaultValue = default(T);
            ColumnWithDefaultValue.DefaultValue = default(T);

            Assert.IsNull(ColumnWithDefaultValue.DefaultValue);
        }

        /// <summary>
        /// Setting default value for deleted column
        /// </summary>
        [TestMethod]
        public void SettingDefaultValueForDeletedColumn()
        {
            if (ColumnWithDefaultValue == null)
            {
                return;
            }

            GenericColumn.Delete();

            try
            {
                ColumnWithDefaultValue.DefaultValue = default(T);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Inserting when default value is set
        /// </summary>
        [TestMethod]
        public void InsertingWhenDefaultValueIsSet()
        {
            if (ColumnWithDefaultValue == null)
            {
                return;
            }

            //Default value
            var defaultValue = GetLargeValue();

            ColumnWithDefaultValue.DefaultValue = defaultValue;

            //Ensuring that table is empty
            Assert.AreEqual(Table.RowCount, 0);

            //Inserting new row
            var rowId = Table.InsertRow();

            //Ensuring that the row was inserted
            Assert.AreEqual(Table.RowCount, 1);

            //Checking that default value was inserted
            var inserted = GenericColumn.EmptyValue();

            Table.ReadRow(rowId, inserted);

            //Column value
            var columnValue = ConvertDbValue(inserted.Value);

            Assert.IsTrue(ValuesAreEqual(columnValue, defaultValue));

            //Setting default value to be null
            ColumnWithDefaultValue.DefaultValue = default(T);

            //Inserting new row
            rowId = Table.InsertRow();

            //Ensuring that the row was inserted
            Assert.AreEqual(Table.RowCount, 2);

            //Checking that null value was inserted
            inserted = GenericColumn.EmptyValue();

            Table.ReadRow(rowId, inserted);

            Assert.IsNull(ConvertDbValue(inserted.Value));
        }

        /// <summary>
        /// Inserting not default value when default value is set
        /// </summary>
        [TestMethod]
        public void InsertingNotDefaultValueWhenDefaultValueIsSet()
        {
            if (ColumnWithDefaultValue == null)
            {
                return;
            }

            //Column default value
            var defaultValue = GetLargeValue();

            ColumnWithDefaultValue.DefaultValue = defaultValue;

            Assert.IsTrue(ValuesAreEqual(ColumnWithDefaultValue.DefaultValue, defaultValue));

            //Value to be inserted
            var valueToBeInserted = GetSmallValue();

            //Inserting new row
            var rowId = Table.InsertRow(GenericColumn.Value(valueToBeInserted));

            //Checking that row was inserted
            Assert.AreEqual(Table.RowCount, 1);

            //Inserted value
            var inserted = GenericColumn.EmptyValue();

            //Reading inserted value
            Table.ReadRow(rowId, inserted);

            Assert.IsTrue(ValuesAreEqual(valueToBeInserted, ConvertDbValue(inserted.Value)));
        }

        /// <summary>
        /// Renaming when default value is set
        /// </summary>
        [TestMethod]
        public void RenamingWhenDefaultValueIsSet()
        {
            if (ColumnWithDefaultValue == null)
            {
                return;
            }

            ColumnWithDefaultValue.DefaultValue = GetLargeValue();

            var newName = StringProperty.RandomIdentifier();

            GenericColumn.Name = newName;

            Assert.AreEqual(GenericColumn.Name, newName.ToLower());
        }

        /// <summary>
        /// Creating two columns with default values (to check if anything collides)
        /// </summary>
        [TestMethod]
        public void CreatingTwoColumnsWithDefaultValue()
        {
            if (ColumnWithDefaultValue == null)
                return;

            var firstValue = GetSmallValue();
            var secondValue = GetLargeValue();

            Assert.IsNull(ColumnWithDefaultValue.DefaultValue);
            Assert.IsNull(AnotherColumnWithDefaultValue.DefaultValue);

            ColumnWithDefaultValue.DefaultValue = firstValue;
            AnotherColumnWithDefaultValue.DefaultValue = secondValue;

            Assert.AreEqual(ColumnWithDefaultValue.DefaultValue, firstValue);
            Assert.AreEqual(AnotherColumnWithDefaultValue.DefaultValue, secondValue);
        }

        ////////////////////////////////////////////////////////////////////
        // Maximum and Minimum

        /// <summary>
        /// Reading and writing maximum
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingMaximum()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Initially maximum must be null
            Assert.IsNull(ColumnWithBounds.Maximum);

            //Setting maximum
            var maximum = GetLargeValue();

            ColumnWithBounds.Maximum = maximum;

            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Maximum, maximum));

            ColumnWithBounds.Maximum = (maximum = GetSmallValue());

            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Maximum, maximum));

            //Setting maximum to null
            ColumnWithBounds.Maximum = default(T);
            ColumnWithBounds.Maximum = default(T);

            Assert.IsNull(ColumnWithBounds.Maximum);
        }

        /// <summary>
        /// Setting maximum for deleted column
        /// </summary>
        [TestMethod]
        public void SettingMaximumForDeletedColumn()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            GenericColumn.Delete();

            try
            {
                ColumnWithBounds.Maximum = default(T);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Inserting value less than maximum
        /// </summary>
        [TestMethod]
        public void InsertingValueLessThanMaximum()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Setting maximum
            ColumnWithBounds.Maximum = GetLargeValue();

            //Inserting value less than maximum
            Table.InsertRow(GenericColumn.Value(GetSmallValue()));

            Assert.AreEqual(Table.RowCount, 1);
        }

        /// <summary>
        /// Inserting value equal to maximum
        /// </summary>
        [TestMethod]
        public void InsertingValueEqualToMaximum()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Setting maximum
            ColumnWithBounds.Maximum = GetLargeValue();

            //Inserting value equal to maximum
            Table.InsertRow(GenericColumn.Value(GetLargeValue()));

            Assert.AreEqual(Table.RowCount, 1);
        }

        /// <summary>
        /// Inserting value greater than maximum
        /// </summary>
        [TestMethod]
        public void InsertingValueGreaterThanMaximum()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Setting maximum
            ColumnWithBounds.Maximum = GetSmallValue();

            try
            {
                //Inserting value greater than maximum
                Table.InsertRow(GenericColumn.Value(GetLargeValue()));
            }
            catch (DbException)
            {
                Assert.AreEqual(Table.RowCount, 0);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Inserting after maximum was cancelled
        /// </summary>
        [TestMethod]
        public void InsertingValueAfterMaximumWasCanceled()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Setting maximum
            ColumnWithBounds.Maximum = GetSmallValue();

            //Cancelling maximum
            ColumnWithBounds.Maximum = default(T);

            //Inserting value
            Table.InsertRow(GenericColumn.Value(GetLargeValue()));
        }

        /// <summary>
        /// Setting maximum after allowed value was inserted
        /// </summary>
        [TestMethod]
        public void SettingMaximumAfterAllowedValueWasInserted()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Inserting value
            var valueToInsert = GetSmallValue();

            Table.InsertRow(GenericColumn.Value(valueToInsert));

            Assert.AreEqual(Table.RowCount, 1);

            //Setting maximum
            var maximum = GetLargeValue();

            ColumnWithBounds.Maximum = maximum;

            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Maximum, maximum));
        }

        /// <summary>
        /// Setting maximum after not allowed value was inserted
        /// </summary>
        [TestMethod]
        public void SettingMaximumAfterNotAllowedValueWasInserted()
        {
            if (ColumnWithBounds == null)
                return;

            //Inserting value
            Table.InsertRow(GenericColumn.Value(GetLargeValue()));

            Assert.AreEqual(Table.RowCount, 1);

            try
            {
                //Setting maximum
                ColumnWithBounds.Maximum = GetSmallValue();
            }
            catch (DbException e)
            {
                Assert.IsNull(ColumnWithBounds.Maximum);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Renaming when maximum is set
        /// </summary>
        [TestMethod]
        public void RenamingWhenMaximumIsSet()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            ColumnWithBounds.Maximum = GetLargeValue();

            var newName = StringProperty.RandomIdentifier();

            GenericColumn.Name = newName;

            Assert.AreEqual(GenericColumn.Name, newName.ToLower());
        }

        /// <summary>
        /// Reading and writing minimum
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingMinimum()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Initially minimum must be null
            Assert.IsNull(ColumnWithBounds.Minimum);

            //Setting minimum
            var minimum = GetSmallValue();

            ColumnWithBounds.Minimum = minimum;

            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Minimum, minimum));

            ColumnWithBounds.Minimum = (minimum = GetLargeValue());

            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Minimum, minimum));

            //Setting minimum to null
            ColumnWithBounds.Minimum = default(T);
            ColumnWithBounds.Minimum = default(T);

            Assert.IsNull(ColumnWithBounds.Minimum);
        }

        /// <summary>
        /// Setting minimum for deleted column
        /// </summary>
        [TestMethod]
        public void SettingMinimumForDeletedColumn()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            GenericColumn.Delete();

            try
            {
                ColumnWithBounds.Minimum = default(T);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Inserting value greater than minimum
        /// </summary>
        [TestMethod]
        public void InsertingValueGreaterThanMinimum()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Setting minimum
            ColumnWithBounds.Minimum = GetSmallValue();

            //Inserting value greater than minimum
            Table.InsertRow(GenericColumn.Value(GetLargeValue()));

            Assert.AreEqual(Table.RowCount, 1);
        }

        /// <summary>
        /// Inserting value equal to minimum
        /// </summary>
        [TestMethod]
        public void InsertingValueEqualToMinimum()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Setting minimum
            ColumnWithBounds.Minimum = GetSmallValue();

            //Inserting value equal to minimum
            Table.InsertRow(GenericColumn.Value(GetSmallValue()));

            Assert.AreEqual(Table.RowCount, 1);
        }

        /// <summary>
        /// Inserting value less than minimum
        /// </summary>
        [TestMethod]
        public void InsertingValueLessThanMinimum()
        {
            if (ColumnWithBounds == null)
                return;

            //Setting minimum
            ColumnWithBounds.Minimum = GetLargeValue();

            try
            {
                //Inserting value equal to minimum
                Table.InsertRow(GenericColumn.Value(GetSmallValue()));
            }
            catch (DbException)
            {
                Assert.AreEqual(Table.RowCount, 0);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Inserting value after minimum was canceled
        /// </summary>
        [TestMethod]
        public void InsertingValueAfterMinimumWasCanceled()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Setting minimum
            ColumnWithBounds.Minimum = GetLargeValue();

            //Cancelling minimum
            ColumnWithBounds.Minimum = default(T);

            Assert.IsNull(ColumnWithBounds.Minimum);

            //Inserting value
            Table.InsertRow(GenericColumn.Value(GetSmallValue()));

            Assert.AreEqual(Table.RowCount, 1);
        }

        /// <summary>
        /// Setting minimum after allowed value was inserted
        /// </summary>
        [TestMethod]
        public void SettingMinimumAfterAllowedValueWasInserted()
        {
            if (ColumnWithBounds == null)
            {
                return;
            }

            //Inserting allowed value
            Table.InsertRow(GenericColumn.Value(GetSmallValue()));

            Assert.AreEqual(Table.RowCount, 1);

            //Setting minimum
            var minimum = GetSmallValue();

            ColumnWithBounds.Minimum = minimum;

            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Minimum, minimum));
        }

        /// <summary>
        /// Setting minimum after not allowed value was inserted
        /// </summary>
        [TestMethod]
        public void SettingMinimumAfterNotAllowedValueWasInserted()
        {
            if (ColumnWithBounds == null)
                return;

            //Inserting allowed value
            Table.InsertRow(GenericColumn.Value(GetSmallValue()));

            Assert.AreEqual(Table.RowCount, 1);

            try
            {
                //Setting minimum
                ColumnWithBounds.Minimum = GetLargeValue();
            }
            catch (DbException)
            {
                Assert.IsNull(ColumnWithBounds.Minimum);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Setting minimum greater than maximum
        /// </summary>
        [TestMethod]
        public void SettingMinumumGreaterThanMaximum()
        {
            if (ColumnWithBounds == null)
                return;

            var minimum = GetLargeValue();
            var maximum = GetSmallValue();

            ColumnWithBounds.Minimum = minimum;
            ColumnWithBounds.Maximum = maximum;

            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Minimum, minimum));
            Assert.IsTrue(ValuesAreEqual(ColumnWithBounds.Maximum, maximum));
        }

        /// <summary>
        /// Renaming when minimum is set
        /// </summary>
        [TestMethod]
        public void RenamingWhenMinimumIsSet()
        {
            if (ColumnWithBounds == null)
                return;

            ColumnWithBounds.Minimum = GetLargeValue();

            var newName = StringProperty.RandomIdentifier();

            GenericColumn.Name = newName;

            Assert.AreEqual(GenericColumn.Name, newName.ToLower());
        }


        /// <summary>
        ///   Renaming a column when it is indexed.
        /// </summary>
        [TestMethod]
        public void RenamingWhenColumnIsIndexed()
        {
            if (GenericColumn.ColumnType == ColumnType.Text
                || GenericColumn.ColumnType == ColumnType.Binary)
                return;

            // Indexing the column
            Table.AddIndex(GenericColumn);

            // Renaming the column
            var newColumnName = StringProperty.RandomIdentifier();

            GenericColumn.Name = newColumnName;

            Assert.AreEqual(GenericColumn.Name, newColumnName.ToLower());
        }
    }
}